﻿// ----------------------------------------------------------------------
// <author name="Moses Kehengu"/>
// <copyright file="DataTransformation.cs" company="Futures Group International">
//     Copyright statement. All right reserved
// </copyright>
//
// ------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Data;
using System.IO;
using System.Security.Cryptography;
using System.IO.Compression;

namespace GenericTools
{
    public static class DataTransformation
    {
        private static SHA1Managed hasher = new SHA1Managed();

        public static string Serialize(DataTable dataTable, bool encrypt)
        {
            DataSet dataset = new DataSet();
            dataset.Tables.Add(dataTable.Copy());
            MemoryStream stream = new MemoryStream();
            dataset.WriteXml(stream, XmlWriteMode.WriteSchema);
            stream.Close();

            byte[] buffer = stream.ToArray();
            string output = UTF8Encoding.UTF8.GetString(buffer);

            if (encrypt) output = Encrypt(output);
            return output;
        }

        public static string Serialize(DataSet dataset, bool encrypt)
        {
            MemoryStream stream = new MemoryStream();
            dataset.WriteXml(stream, XmlWriteMode.WriteSchema);
            stream.Close();

            byte[] buffer = stream.ToArray();
            string output = UTF8Encoding.UTF8.GetString(buffer);

            if (encrypt) output = Encrypt(output);
            output = Compress(output);
            return output;
        }

        public static string Serialize(DataTable dataTable)
        {
            string output = Serialize(dataTable, true);
            return output;
        }

        public static DataTable Deserialize(string data, bool decrypt)
        {
            if (decrypt) data = Decrypt(data);
            byte[] buffer = UTF8Encoding.UTF8.GetBytes(data);
            MemoryStream stream = new MemoryStream(buffer);

            DataSet dataSet = new DataSet();
            dataSet.ReadXml(stream, XmlReadMode.ReadSchema);
            stream.Close();

            DataTable dataTable = dataSet.Tables[0].Copy();
            return dataTable;
        }

        public static DataSet DeserializeDataSet(string compressedData, bool decrypt)
        {
            string data = Decompress(compressedData);
            if (decrypt) data = Decrypt(data);
            byte[] buffer = UTF8Encoding.UTF8.GetBytes(data);
            MemoryStream stream = new MemoryStream(buffer);

            DataSet dataSet = new DataSet();
            dataSet.ReadXml(stream, XmlReadMode.ReadSchema);
            stream.Close();

            return dataSet;
        }

        public static DataTable Deserialize(string data)
        {
            DataTable dataTable = Deserialize(data, true);
            return dataTable;
        }

        public static string Encrypt(string plainText)
        {
            byte[] key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            try
            {
                byte[] sourceDataBytes = ASCIIEncoding.ASCII.GetBytes(plainText);
                MemoryStream tempStream = new MemoryStream();

                DESCryptoServiceProvider encryptor = new DESCryptoServiceProvider();
                CryptoStream encryptionStream = new CryptoStream(
                    tempStream,
                    encryptor.CreateEncryptor(key, iv),
                    CryptoStreamMode.Write);
                encryptionStream.Write(sourceDataBytes, 0, sourceDataBytes.Length);
                encryptionStream.FlushFinalBlock();

                byte[] encryptedDataBytes = tempStream.GetBuffer();
                string cipherText = Convert.ToBase64String(encryptedDataBytes, 0, (int)tempStream.Length);
                return cipherText;
                
                //string compressed = Compress(cipherText);
                //return compressed;
            }
            catch
            {
                return String.Empty;
            }
        }

        public static string Decrypt(string cipherText)
        {
            //cipherText = Decompress(cipherText);

            byte[] key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            try
            {
                byte[] encryptedDataBytes = Convert.FromBase64String(cipherText);
                MemoryStream tempStream = new MemoryStream(encryptedDataBytes, 0, encryptedDataBytes.Length);

                DESCryptoServiceProvider decryptor = new DESCryptoServiceProvider();
                CryptoStream decryptionStream = new CryptoStream(tempStream,
                    decryptor.CreateDecryptor(key, iv),
                    CryptoStreamMode.Read);

                StreamReader allDataReader = new StreamReader(decryptionStream);
                string plainText = allDataReader.ReadToEnd();
                return plainText;
            }
            catch
            {
                return String.Empty;
            }
        }

        public static string Hash(string password)
        {
            byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password);
            byte[] passwordHash = hasher.ComputeHash(passwordBytes);
            string hash = Convert.ToBase64String(passwordHash, 0, passwordHash.Length);
            return hash;
        }

        /// <summary>
        /// Compresses the string
        /// </summary>
        /// <param name="text">The text</param>
        /// <returns>Compressed text</returns>
        public static string Compress(string text)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(text);
            MemoryStream memoryStream = new MemoryStream();
            using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
            {
                gZipStream.Write(buffer, 0, buffer.Length);
            }

            memoryStream.Position = 0;
            var compressedData = new byte[memoryStream.Length];
            memoryStream.Read(compressedData, 0, compressedData.Length);

            var gZipBuffer = new byte[compressedData.Length + 4];
            Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length);
            Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4);
            string output = Convert.ToBase64String(gZipBuffer);
            return output;
        }

        public static string Compress(FileInfo fileInfo)
        {
            string outputFilename = String.Empty;
            // Get the stream of the source file.
            using (FileStream inFile = fileInfo.OpenRead())
            {
                // Prevent compressing hidden and already compressed files.
                if ((File.GetAttributes(fileInfo.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fileInfo.Extension != ".gz")
                {
                    // Create the compressed file.
                    using (FileStream outFile = File.Create(fileInfo.FullName + ".gz"))
                    {
                        using (GZipStream compress = new GZipStream(outFile, CompressionMode.Compress))
                        {
                            // Copy the source file into the compression stream.
                            inFile.CopyTo(compress);
                            outputFilename = fileInfo.FullName + ".gz";
                        }
                    }
                }
            }
            return outputFilename;
        }

        public static string Decompress(FileInfo fi)
        {
            // Get the stream of the source file.
            using (FileStream inFile = fi.OpenRead())
            {
                // Get original file extension, for example "doc" from report.doc.gz.
                string curFile = fi.FullName;
                string origName = curFile.Remove(curFile.Length - fi.Extension.Length);

                // Create the decompressed file.
                using (FileStream outFile = File.Create(origName))
                {
                    using (GZipStream decompress = new GZipStream(inFile, CompressionMode.Decompress))
                    {
                        // Copy the decompression stream into the output file.
                        // The following is only applicable in .NET 4
                        decompress.CopyTo(outFile);
                        return origName;
                    }
                }
            }
        }

        /// <summary>
        /// Decompresses the string
        /// </summary>
        /// <param name="compressText">Compressed text</param>
        /// <returns>Uncompressed text</returns>
        public static string Decompress(string compressText)
        {
            byte[] gZipBuffer = Convert.FromBase64String(compressText);
            using (var memoryStream = new MemoryStream())
            {
                int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
                memoryStream.Write(gZipBuffer, 4, gZipBuffer.Length - 4);

                var buffer = new byte[dataLength];
                memoryStream.Position = 0;
                using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
                {
                    gZipStream.Read(buffer, 0, buffer.Length);
                }
                string output = Encoding.UTF8.GetString(buffer);
                return output;
            }            
        }

        /// <summary>
        /// Extension method for copying data between streams
        /// </summary>
        /// <param name="source">Source Stream</param>
        /// <param name="destination">Destination Stream</param>
        /// <returns>Number of bytes copied</returns>
        public static long CopyTo(this Stream source, Stream destination)
        {
            byte[] buffer = new byte[2048];
            int bytesRead;
            long totalBytes = 0;
            while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
            {
                destination.Write(buffer, 0, bytesRead);
                totalBytes += bytesRead;
            }
            return totalBytes;
        }

    }
}